/**
	Pulsar engine. Core.
	Standard file streams

	Copyright (C) 2013-2014 Dmitry Dolgov

	This program is free software; you can redistribute it and/or modify it under the terms of the
	GNU General Public License as published by the Free Software Foundation; either version 2 of
	the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
	without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
	See the GNU General Public License for more details.
*/

#include <pulsar/pulsar.h>

namespace pl
{

/**
	Reading strings with encoding and autobyte swap (if need)
*/
template <typename T> bool _readStringEncode(StreamRead* s, pl_char* pStr, int maxLen, pl_char termChar, bool bSwap)
{
	int i;
	T value;

	if (pStr == NULL)
	{
		PL_ASSERTW(FALSE, PL_T("Wrong arguments"));
		return false;
	}
	for (i=0; i<maxLen-1; i++)
	{
		if (!s->read(value))
		{
			pStr[i] = PL_T('\0');
			return i > 0 ? true : false;
		}
		if (bSwap)
		{
			pl::swapBytes(value);
		}
		convertChar(value, pStr[i]);
		if (pStr[i] == termChar)
		{
			break;
		}
	}
	pStr[i+1] = PL_T('\0');
	return true;
}

/**
	Writing strings with encoding and autobyte swap (if need)
*/
template <typename T> bool _writeStringEncode(StreamWrite* s, const pl_char* pStr, int size, bool bSwap)
{
	int i;
	T value;

	if (pStr == NULL)
	{
		PL_ASSERTW(FALSE, PL_T("Wrong arguments"));
		return false;
	}
	for (i=0; i<size; i++)
	{
		convertChar(pStr[i], value);
		if (bSwap)
		{
			pl::swapBytes(value);
		}
		if (!s->write(value))
		{
			return false;
		}
	}
	return true;
}

} // namespace pl

/**
	\param pStr pointer to buffer to receive strings
	\param maxLen buffer length (in chars)
	\param termChar termination character (usually \0 or \n)
	Reading string with the current application encoding (ANSI/UNICODE/UIIODE32)
*/
bool pl::StreamRead::readString(pl_char* pStr, int maxLen, pl_char termChar /*= '\0'*/)
{
	return _readStringEncode<pl_char>(this, pStr, maxLen, termChar, false);
}

bool pl::StreamRead::readStringEncode(pl_char* pStr, int maxLen, EncodeMode mode, pl_char termChar /*= PL_T('\0')*/)
{
	switch (mode)
	{
		case ENCODE_MODE_DEFAULT:
			return readString(pStr, maxLen, termChar);
		case ENCODE_MODE_ANSI:
			return _readStringEncode<char>(this, pStr, maxLen, termChar, false);
		case ENCODE_MODE_UNICODE_LE:
			return _readStringEncode<wchar_t>(this, pStr, maxLen, termChar, false);
		case ENCODE_MODE_UNICODE_BE:
			return _readStringEncode<wchar_t>(this, pStr, maxLen, termChar, true);
		case ENCODE_MODE_UTF8:
			// Unicode reader will be implemented later
			break;
		case ENCODE_MODE_UNICODE32_LE:
			return _readStringEncode<pl_uint32>(this, pStr, maxLen, termChar, false);
		case ENCODE_MODE_UNICODE32_BE:
			return _readStringEncode<pl_uint32>(this, pStr, maxLen, termChar, true);
		default:
			PL_ASSERTE(FALSE, PL_T("Bad encode mode"));
			return false;
	}

	if (pStr == NULL)
	{
		PL_ASSERTW(FALSE, PL_T("Wrong arguments"));
		return false;
	}
	if (isEOF())
	{
		return false;
	}

	pl_char8* buffer = (pl_char8*)alloca(maxLen*4+1);
	int i;

	// Reading string till terminator
	for (i=0; i<maxLen-1; i++)
	{
		if (!read(buffer[i]))
		{
			buffer[i] = PL_T('\0');
			break;
		}
		if (buffer[i] == termChar)
		{
			buffer[i] = PL_T('\0');
			break;
		}
	}
	pl::convertStr(buffer, pStr, maxLen);
	return true;
}

/**
	\param sizeBytes total amount of records in bytes
	\return false, if error occured
	Write zero sequence
*/
bool pl::StreamWrite::writeZero(pl_int64 sizeBytes)
{
	pl_uint8 buffer[1024];
	memset(buffer, 0, (size_t)pl::min((pl_int64)1024, sizeBytes));

	// Write by blocs 1024 bytes max
	while (sizeBytes > 0)
	{
		pl_int32 currentSize = (pl_int32)pl::min((pl_int64)1024, sizeBytes);
		if (!writeBuffer(buffer, 1, currentSize))
		{
			return false;
		}
		sizeBytes-=currentSize;
	}
	return true;
}

/**
	\param pStr pointer to buffer to receive strings
	\param termChar termination character (usually \0 or \n)
	Wriring string in ANSI format
*/
bool pl::StreamWrite::writeStringEncode(const pl_char* pStr, EncodeMode mode, pl_char termChar /*= PL_T('\0')*/)
{
	int len;

	if (pStr == NULL)
	{
		PL_ASSERTE(FALSE, PL_T("Bad arguments"));
		return false;
	}
	for (len=0; pStr[len] != termChar; len++)
		; // do nothing
	len++; // Last character will be written into the stream

	switch (mode)
	{
		case ENCODE_MODE_DEFAULT:
			return writeString(pStr, termChar);
		case ENCODE_MODE_ANSI:
			return _writeStringEncode<char>(this, pStr, len, false);
		case ENCODE_MODE_UNICODE_LE:
			return _writeStringEncode<wchar_t>(this, pStr, len, false);
		case ENCODE_MODE_UNICODE_BE:
			return _writeStringEncode<wchar_t>(this, pStr, len, true);
		case ENCODE_MODE_UTF8:
			// Will be implemented in this function below
			break;
		case ENCODE_MODE_UNICODE32_LE:
			return _writeStringEncode<pl_uint32>(this, pStr, len, false);
		case ENCODE_MODE_UNICODE32_BE:
			return _writeStringEncode<pl_uint32>(this, pStr, len, true);
		default:
			PL_ASSERTE(FALSE, PL_T("Bad encode mode"));
			return false;
	}

	pl_int32 i;

	for (i=0; i<len; i++)
	{
		pl_char8 buffer[4];
		pl_uint32 size = convertChar(*pStr, buffer);

		if (size == 0)
		{
			return false;
		}
		if (!writeBuffer(buffer, 1, size))
		{
			return false;
		}
		pStr++;
	}
	return true;
}

//
// End of file 'pulsar_file_stream_std.cpp'
//
